<template>
  <div class="ele-upload-image">
    <!-- 可放大查看和删除 -->
    <ele-gallery
      :lazy="lazy"
      :remove-fn="handleRemove"
      :isShowUploadIcon="isShowUploadIcon"
      :isShowDelete="isShowDelete"
      :size="size"
      :sliceSingle="true"
      :source="gallerySource"
      :thumbSuffix="thumbSuffix"
      :title="title"
      :isShowBtn="isShowBtn"
    ></ele-gallery>
    <!-- 上传组件 -->
    <el-upload
      :accept="accept"
      :action="actionUrl"
      :before-upload="handleBeforeUpload"
      :data="data"
      :file-list="fileList"
      :disabled="disabled"
      :drag="Boolean(drag)"
      :headers="headers"
      :http-request="httpRequest"
      :limit="limit"
      :list-type="drag ? 'picture' : 'picture-card'"
      :multiple="multiple"
      :name="name"
      :on-change="handleChange"
      :on-error="handleUploadError"
      :on-exceed="handleExceed"
      :on-success="handleUploadSuccess"
      :show-file-list="isShowFileList"
      :style="{
        marginBottom: computedValues.length && multiple ? '0px' : '0px',
      }"
      :withCredentials="withCredentials"
      :on-progress="handleUploadProcess"
      ref="upload"
      v-if="!crop"
      v-show="isShowUploadIcon"
    >
      <slot>
        <div v-loading="uploading" v-if="isShowUploadIcon">
          <!-- drag显示 -->
          <template v-if="drag">
            <Plus />
            <div class="el-upload__text">
              将文件拖到此处，或
              <em>点击上传</em>
            </div>
          </template>

          <!-- 非drag -->
          <template v-else>
            <div
              :style="{
                width: size + 'px',
                height: size + 'px',
                lineHeight: size + 'px',
                textAlign: 'center',
                borderWidth: '1px',
                borderStyle: 'dashed',
                borderColor: '#909399',
                borderRadius: '6px',
              }"
            >
              <el-icon :size="40" style="vertical-align: middle">
                <Plus />
              </el-icon>
            </div>
          </template>
          <el-progress v-if="uploading" :percentage="Number(loadProgress)"></el-progress>
        </div>
      </slot>
    </el-upload>

    <div v-if="crop">
      <div
        :style="{
          width: size + 'px',
          height: size + 'px',
          lineHeight: size + 'px',
        }"
        @click="isShowCrop = true"
        class="el-upload el-upload--picture-card"
        style="margin-bottom: 20px"
        v-show="isShowUpload"
      >
        <i class="el-icon-plus avatar-uploader-icon"></i>
      </div>

      <cropper
        :field="name"
        :headers="headers"
        :height="cropHeight"
        :noCircle="cropHeight !== cropWidth"
        :params="data"
        :url="actionUrl"
        :width="cropWidth"
        :withCredentials="withCredentials"
        @crop-success="handleCropSuccess"
        @crop-upload-fail="handleCropUploadError"
        @crop-upload-success="handleCropUploadSuccess"
        @src-file-set="handleSetFileSet"
        class="ele-upload-image--cropper"
        img-format="png"
        ref="cropper"
        v-if="isShowCrop"
        v-model="isShowCrop"
      ></cropper>
    </div>
  </div>
</template>

<script setup name="EleUploadImage">
  import Cropper from 'vue-image-crop-upload';
  import EleGallery from '@/components/EleGallery/EleGallery';
  import { getToken } from '@/utils/auth';
  import { download } from '@/utils/request';
  import { watch } from 'vue';

  const { proxy } = getCurrentInstance();

  const props = defineProps({
    // 值
    value: {
      type: [String, Array],
      default() {
        return [];
      },
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    // 请求路径
    uploadUrl: {
      type: String,
      default: '/resource/oss/upload',
    },
    // 是否显示按钮
    isShowBtn: {
      type: Boolean,
      default: true,
    },
    // 是否剪裁
    crop: {
      type: Boolean,
      default: false,
    },
    //是否显示上传按钮
    isShowUploadIcon: {
      type: Boolean,
      default: true,
    },
    //是否显示删除按钮
    isShowDelete: {
      type: Boolean,
      default: true,
    },
    // 图片显示大小
    size: {
      type: Number,
      default: 80,
    },
    // 裁剪高度
    cropHeight: {
      type: Number,
    },
    // 裁剪宽度
    cropWidth: {
      type: Number,
    },
    // 大小限制(MB)
    fileSize: {
      type: Number,
      default: 50,
    },
    // 响应处理函数
    responseFn: Function,
    // 文件类型, 例如['png', 'jpg', 'jpeg']
    fileType: {
      type: Array,
      default: () => ['pdf', 'doc', 'docx'],
    },
    // 是否显示上传成功的提示
    isShowSuccessTip: {
      type: Boolean,
      default: true,
    },
    //是否显示列表
    isShowFileList: {
      type: Boolean,
      default: false,
    },
    // 缩略图后缀, 例如七牛云缩略图样式 (?imageView2/1/w/20/h/20)
    thumbSuffix: {
      type: String,
      default: '',
    },
    // 是否显示提示
    isShowTip: {
      type: Boolean,
      default: true,
    },
    // 弹窗标题
    title: String,
    // 图片懒加载
    lazy: {
      type: Boolean,
      default: false,
    },
    // 上传地址 (同官网)
    // action: {
    //   type: String,
    //   required: true
    // },
    // 设置上传的请求头部(同官网)
    // headers: Object,
    // 文件个数显示(同官网)
    limit: {
      type: Number,
      default: 9,
    },
    // 是否启用拖拽上传 (同官网)
    drag: {
      type: Boolean,
      default: false,
    },
    // 	支持发送 cookie 凭证信息 (同官网)
    withCredentials: {
      type: Boolean,
      default: false,
    },
    // 是否支持多选文件 (同官网)
    multiple: {
      type: Boolean,
      default: false,
    },
    // 上传时附带的额外参数(同官网)
    data: Object,
    // 上传的文件字段名 (同官网)
    name: {
      type: String,
      default: 'file',
    },
    // 覆盖默认的上传行为，可以自定义上传的实现 (同官网)
    httpRequest: Function,
    // 接受上传的文件类型（thumbnail-mode 模式下此参数无效）(同官网)
    accept: String,
    // 删除前的操作
    beforeRemove: Function,
  });
  const {
    value,
    uploadUrl,
    crop,
    isShowUploadIcon,
    isShowDelete,
    size,
    cropHeight,
    cropWidth,
    fileSize,
    responseFn,
    fileType,
    isShowSuccessTip,
    isShowFileList,
    thumbSuffix,
    isShowTip,
    title,
    lazy,
    limit,
    drag,
    withCredentials,
    multiple,
    data,
    name,
    accept,
    beforeRemove,
  } = toRefs(props);

  const state = reactive({
    gallerySource: [],
    cropData: {},
    isShowCrop: false,
    uploading: false,
    loadProgress: 0,
    actionUrl: process.env.VITE_APP_BASE_API + props.uploadUrl,
    fileList: [],
  });
  const { gallerySource, cropData, isShowCrop, uploading, loadProgress, actionUrl, fileList } = toRefs(state);
  const emit = defineEmits(['error', 'update:value', 'success']);

  const headers = computed(() => {
    if (getToken()) {
      return {
        Authorization: 'Bearer ' + getToken(),
      };
    }
  });
  const showTip = computed(() => {
    return props.isShowTip && (props.fileType.length || props.fileSize);
  });
  const computedValues = computed(() => {
    if (props.value) {
      if (typeof props.value === 'string') {
        let tmp = {};
        tmp.id = props.value.id;
        tmp.name = props.value.name;
        tmp.src = props.value;
        let fileExtension = getFileExtension(props.value);
        if (fileExtension == 'doc' || fileExtension == 'docx') {
          tmp.thumb = '/images/doc.svg';
        } else if (fileExtension == 'xls' || fileExtension == 'xlsx') {
          tmp.thumb = '/images/xls.svg';
        } else if (fileExtension == 'ppt' || fileExtension == 'pptx') {
          tmp.thumb = '/images/ppt.svg';
        } else if (fileExtension == 'pdf') {
          tmp.thumb = '/images/pdf.svg';
        } else if (fileExtension == 'mp4') {
          tmp.thumb = '/images/mp4.svg';
        } else if (
          fileExtension == 'avi' ||
          fileExtension == 'wmv' ||
          fileExtension == 'mov' ||
          fileExtension == 'flv' ||
          fileExtension == 'rmvb' ||
          fileExtension == '3gp' ||
          fileExtension == 'm4v' ||
          fileExtension == 'mkv'
        ) {
          tmp.thumb = '/images/video.svg';
        } else if (fileExtension == 'rar') {
          tmp.thumb = '/images/rar.svg';
        } else if (fileExtension == 'zip') {
          tmp.thumb = '/images/zip.svg';
        } else if (fileExtension == 'png' || fileExtension == 'jpg' || fileExtension == 'jpeg') {
          tmp.thumb = props.value;
        }
        state.gallerySource = [tmp];
        return [tmp]; //[this.value];
      } else {
        state.gallerySource = props.value.map(item => {
          let tmp = {};
          tmp.id = item.id;
          tmp.name = item.name;
          if (typeof item === 'object') {
            if (item.name !== undefined) {
              tmp.title = item.name;
            }
            if (item.url !== undefined) {
              tmp.src = item.url;
              let fileExtension = getFileExtension(item.url);
              if (fileExtension == 'doc' || fileExtension == 'docx') {
                tmp.thumb = '/images/doc.svg';
              } else if (fileExtension == 'xls' || fileExtension == 'xlsx') {
                tmp.thumb = '/images/xls.svg';
              } else if (fileExtension == 'ppt' || fileExtension == 'pptx') {
                tmp.thumb = '/images/ppt.svg';
              } else if (fileExtension == 'pdf') {
                tmp.thumb = '/images/pdf.svg';
              } else if (fileExtension == 'mp4') {
                tmp.thumb = '/images/mp4.svg';
              } else if (
                fileExtension == 'avi' ||
                fileExtension == 'wmv' ||
                fileExtension == 'mov' ||
                fileExtension == 'flv' ||
                fileExtension == 'rmvb' ||
                fileExtension == '3gp' ||
                fileExtension == 'm4v' ||
                fileExtension == 'mkv'
              ) {
                tmp.thumb = '/images/video.svg';
              } else if (fileExtension == 'rar') {
                tmp.thumb = '/images/rar.svg';
              } else if (fileExtension == 'zip') {
                tmp.thumb = '/images/zip.svg';
              } else if (fileExtension == 'png' || fileExtension == 'jpg' || fileExtension == 'jpeg') {
                tmp.thumb = item.url;
              }
            }
          } else {
            tmp.src = item;
            let fileExtension = getFileExtension(item);
            if (fileExtension == 'doc' || fileExtension == 'docx') {
              tmp.thumb = '/images/doc.svg';
            } else if (fileExtension == 'xls' || fileExtension == 'xlsx') {
              tmp.thumb = '/images/xls.svg';
            } else if (fileExtension == 'ppt' || fileExtension == 'pptx') {
              tmp.thumb = '/images/ppt.svg';
            } else if (fileExtension == 'pdf') {
              tmp.thumb = '/images/pdf.svg';
            } else if (fileExtension == 'mp4') {
              tmp.thumb = '/images/mp4.svg';
            } else if (
              fileExtension == 'avi' ||
              fileExtension == 'wmv' ||
              fileExtension == 'mov' ||
              fileExtension == 'flv' ||
              fileExtension == 'rmvb' ||
              fileExtension == '3gp' ||
              fileExtension == 'm4v' ||
              fileExtension == 'mkv'
            ) {
              tmp.thumb = '/images/video.svg';
            } else if (fileExtension == 'rar') {
              tmp.thumb = '/images/rar.svg';
            } else if (fileExtension == 'zip') {
              tmp.thumb = '/images/zip.svg';
            } else if (fileExtension == 'png' || fileExtension == 'jpg' || fileExtension == 'jpeg') {
              tmp.thumb = item;
            }
          }
          return tmp;
        });
        return [...props.value];
      }
    } else {
      state.gallerySource = [];
      return [];
    }
  });
  const isShowUpload = computed(() => {
    if (props.multiple) {
      return true;
    } else {
      return computedValues.value.length === 0;
    }
  });
  const successFiles = computed(() => {
    return state.fileList.filter(file => file.status === 'success');
  });
  watchEffect(() => {
    if (state.isShowCrop === false) {
      state.cropData = {};
    }
  });
  const getFileExtension = urlStr => {
    let fileExtension = '';
    if (urlStr.lastIndexOf('.') > -1) {
      fileExtension = urlStr.slice(urlStr.lastIndexOf('.') + 1);
      fileExtension = fileExtension.toLowerCase();
    }
    return fileExtension;
  };
  const handleSetFileSet = (fileName, fileType, fileSize) => {
    const uid = state.cropData.uid || new Date().getTime();
    state.cropData = {
      name: fileName,
      percentage: 0,
      size: fileSize,
      type: fileType,
      status: 'ready',
      uid: uid,
    };
  };
  const handleCropSuccess = b64Data => {
    state.cropData.url = b64Data;
  };
  const handleCropUploadError = status => {
    proxy.$modal.msgError('上传失败, 请重试');
    emit('error', status);
  };
  const handleCropUploadSuccess = response => {
    state.cropData.status = 'success';
    state.cropData.percentage = 100;
    state.cropData.response = response;
    const file = Object.assign({}, state.cropData);
    let index = state.fileList.findIndex(item => item.uid === file.uid);
    if (index > -1) {
      state.fileList.splice(index, 1, file);
    } else {
      state.fileList.push(file);
    }
    handleUploadSuccess(response, file, state.fileList);
  };
  // 上传前校检格式和大小
  const handleBeforeUpload = file => {
    let isValid = false;
    if (props.fileType.length) {
      let fileExtension = '';
      if (file.name.lastIndexOf('.') > -1) {
        fileExtension = file.name.slice(file.name.lastIndexOf('.') + 1);
      }
      isValid = props.fileType.some(type => {
        // if (file.type.indexOf(type) > -1) return true;
        if (fileExtension && fileExtension.indexOf(type) > -1) return true;
        return false;
      });
    } else {
      isValid = file.type.indexOf('image') > -1;
    }
    if (!isValid) {
      proxy.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join('/')}格式文件!`);
      return false;
    }

    if (props.fileSize) {
      const isLt = file.size / 1024 / 1024 < props.fileSize;
      if (!isLt) {
        proxy.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);
        return false;
      }
    }
    state.fileList = state.fileList.filter(file => file.status === 'ready')
    if(state.fileList.length + computedValues.value.length > props.limit) {
      handleExceed()
      handleDeleteFile(file)
      return false
    }

    state.uploading = true;
    return true;
  };
  const handleChange = (file, fileList) => {
    state.uploading = false;
    state.fileList = fileList;
  };
  // 文件个数超出
  const handleExceed = () => {
    proxy.$modal.msgError(`最多上传${limit.value}个文件`);
  };
  // 上传失败
  const handleUploadError = err => {
    state.uploading = false;
    proxy.$modal.msgError('上传失败, 请重试');
    emit('error', err);
  };
  // 上传成功回调
  const handleUploadSuccess = (response, file) => {
    let url = response;
    // if (response.code != '200') {
    //   proxy.$modal.msgError(response.msg);
    //   return;
    // }
    state.uploading = false;
    if (props.isShowSuccessTip) {
      proxy.$modal.msgSuccess('上传成功');
      emit('success');
    }
    if (props.responseFn) {
      url = props.responseFn(response, file, successFiles.value);
    }
    if (props.multiple) {
      if (Array.isArray(props.value)) {
        emit('update:value', [...props.value, url]);
        handleDeleteFile(file);
      } else {
        emit('update:value', [url]);
      }
    } else {
      emit('update:value', [url]);
    }
  };
  const doRemove = index => {
    if (props.multiple) {
      const data = [...computedValues.value];
      data.splice(index, 1);
      emit('update:value', data || []);
      state.fileList = [];
    } else {
      const data = [...computedValues.value];
      data.splice(index, 1);
      emit('update:value', []);
      state.fileList = [];
    }
  };
  const handleRemove = index => {
    if (!props.beforeRemove) {
      doRemove(index);
    } else if (typeof props.beforeRemove === 'function') {
      const file = props.multiple ? computedValues.value[index] : computedValues.value[0];

      const before = props.beforeRemove(file, computedValues.value);
      if (before && before.then) {
        before.then(
          () => {
            doRemove(index);
          },
          () => {}
        );
      } else if (before !== false) {
        doRemove(index);
      }
    }
  };
  const handleDeleteFile = (file) => {
    let index = state.fileList.findIndex(item => item.uid == file.uid)

    if(index != -1) {
      state.fileList.splice(index,1)
    }
  };
  const handleUploadProcess = (event, file, fileList) => {
    state.uploading = true; // 显示进度条
    state.loadProgress = file.percentage.toFixed(0); // 动态获取文件上传进度
    if (state.loadProgress >= 100) {
      state.loadProgress = 100;
    }
  };
</script>

<style>
  .ele-upload-image {
    line-height: 1;
    display: flex;
    justify-content: flex-start;
    align-items: center;
  }
  .ele-upload-image .el-loading-spinner {
    line-height: 1;
  }
  .ele-upload-image .el-icon-plus {
    vertical-align: middle;
  }
  .ele-upload-image .el-upload--picture-card {
    width: auto;
    height: auto;
    background: #fbfdff !important;
    line-height: inherit;
    border: none;
  }
  .gallery-source-title {
    font-size: 10px;
    text-align: center;
  }
  /* 裁剪 */
  .vue-image-crop-upload.ele-upload-image--cropper {
    z-index: 99;
  }
  .ele-upload-image--cropper .vicp-drop-area {
    background-color: #fbfdff !important;
  }
  .ele-upload-image--cropper .vicp-icon1-arrow {
    border-bottom-color: #909399 !important;
  }
  .ele-upload-image--cropper .vicp-icon1-body {
    background-color: #909399 !important;
  }
  .ele-upload-image--cropper .vicp-icon1-bottom {
    border-color: #909399 !important;
  }
  .ele-gallery .el-upload-list--picture-card .el-upload-list__item {
    font-size: 14px !important;
  }
</style>
